#ifndef __SHARED_PTR_H__
#define __SHARED_PTR_H__

#include <iostream>
#include <functional>

namespace wqc
{
// 可进行拷贝版本的智能指针实现
// 通过引入引用计数来解决智能指针的拷贝问题
template <typename TYPE>
class shared_ptr
{
public:
    shared_ptr(TYPE* ptr):
        m_ptr(ptr)
    {
        try
        {
            m_refcnt_ptr = new int(1);
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }

        std::cout << "shared_ptr(TYPE* ptr)" << ", this=" << this << ", reference_count=" << *m_refcnt_ptr << std::endl;
    }

    template <typename Deleter>
    shared_ptr(TYPE* ptr, Deleter deleter):
        m_ptr(ptr),
        m_deleter(deleter)
    {
        try
        {
            m_refcnt_ptr = new int(1);
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }

        std::cout << "shared_ptr(TYPE* ptr)" << ", this=" << this << ", reference_count=" << *m_refcnt_ptr << std::endl;
    }

    ~shared_ptr()
    {
        std::cout << "~shared_ptr" << ", this=" << this << ", reference_count=" << *m_refcnt_ptr << std::endl;
        __release();
    }

    shared_ptr(const shared_ptr<TYPE>& other):
        m_ptr(other.m_ptr),
        m_refcnt_ptr(other.m_refcnt_ptr)
    {
        (*m_refcnt_ptr)++;

        std::cout << "shared_ptr(const shared_ptr<TYPE>& other)" << ", this=" << this << ", reference_count=" << *m_refcnt_ptr << std::endl;
    }

    shared_ptr<TYPE>& operator=(const shared_ptr<TYPE>& other)
    {
        if (other.m_ptr != m_ptr)
        {
            __release();
            this->m_ptr = other.m_ptr;
            this->m_refcnt_ptr = other.m_refcnt_ptr;
            (*this->m_refcnt_ptr)++;
        }

        std::cout << "operator=" << ", this=" << this << ", reference_count=" << *m_refcnt_ptr << std::endl;

        return *this;
    }

    TYPE& operator*()
    {
        return *m_ptr;
    }

    TYPE* operator->()
    {
        return m_ptr;
    }

    int reference_count()
    {
        return *m_refcnt_ptr;
    }

    TYPE* get() const
    {
        return m_ptr;
    }

private:
    void __release()
    {
        if (--(*m_refcnt_ptr) == 0)
        {
            m_deleter(m_ptr);
            delete m_refcnt_ptr;
        }
    }
private:
    /* data */
    TYPE* m_ptr;
    int* m_refcnt_ptr;
    std::function<void (TYPE*)> m_deleter = [](TYPE* ptr){delete ptr;};
};

}

#endif /* end of __SHARED_PTR_H__ */